/*
 * Created on Feb 23, 2004
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package ModuleBuilder.UI.tree;

import java.util.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.EtchedBorder;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import ModuleBuilder.*;
import ModuleBuilder.model.*;
import java.beans.*;

/**
 * @author Milton  Soong
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 * 
 * This is the unit editor class
 */
public class UnitProfileEditor extends JPanel 
	implements ActionListener, iEditorPanel {
	
	private static final String CLEAR_CMD="Clear".intern();	// clear all the fields w/o saving
	private static final String SAVE_CMD="Save".intern();	// save and create a new node (if does not exist previously) with current data
	private static final String COPY_CMD="Copy".intern();	// copy and make a new unit type based on the selected one
															// this is NOT valid on the main editor window
	private static final String ADD_CMD="Add Attribute".intern();	// add a new attribute to this
	private static final String DELETE_CMD="Delete".intern();	// this is NOT valid on the main editor window
	public static final String DEFAULT_NAME="default".intern(); 
	
	private JTextField unTextField;	// the user given name of the unit "type", i.e. "Rohan Cavalry".
	private ArrayList attributes;	// an array list that holds a bunch of AttributePanel.
	private JPanel allAttribPanel;	// a panel that can grow or shrink in size to contain each unit type attribute
	private JScrollPane allAttribScrollPane; 
	private UnitProfileModel model;	// the Current model being operated at.
	private DefaultMutableTreeNode editorTreeNode;	// the node where the editor is located
	private GameTreeEditor gameTreeEditor;	
	
	JButton clearButton;
	JButton saveButton;
	JButton copyButton;
	JButton addAttribButton;
	JButton deleteButton;
	
	/**
	 * Hashmap that contains a button object ==> AttribPanel lookup
	 * This data structure needs to be reset everytime a new model is set into the UI
	 * and we'll have to build the hashMap from scratch.
	 */
	HashMap attribPanelHash;
									
	
	public UnitProfileEditor(GameTreeEditor in) {
		gameTreeEditor = in;
		attributes = new ArrayList();
		attribPanelHash = new HashMap();
		JLabel unLabel = new JLabel("Unit Profile Name: ");
		unTextField = new JTextField();
		
		// First setup the UNit Type Layout on top
		JPanel typeNameInputPanel = new JPanel();
		typeNameInputPanel.setLayout(new BoxLayout(typeNameInputPanel, BoxLayout.LINE_AXIS));
		typeNameInputPanel.add(unLabel);
		typeNameInputPanel.add(unTextField);
		
		// now need to set some size limitations to the components
		Dimension d = unTextField.getPreferredSize();
		d.width = 200;
		unTextField.setMinimumSize(d);
		unTextField.setPreferredSize(d);
		unTextField.setMaximumSize(d);


		// Next the main body which is enclosed inside of a scrollable panel
		// the mainbody always contains a Grid of AttributePanel
		//
		allAttribPanel = new JPanel();
		allAttribPanel.setLayout(new BoxLayout(allAttribPanel, BoxLayout.PAGE_AXIS));
		
		allAttribScrollPane = new JScrollPane(allAttribPanel);
		
		// Create the Button panel in the bottom
		clearButton = new JButton(CLEAR_CMD);
		clearButton.setActionCommand(CLEAR_CMD);
		clearButton.addActionListener(this);
 
		saveButton = new JButton(SAVE_CMD);
		saveButton.setActionCommand(SAVE_CMD);
		saveButton.addActionListener(this);
 
		copyButton = new JButton(COPY_CMD);
		copyButton.setActionCommand(COPY_CMD);
		copyButton.addActionListener(this);
 
		addAttribButton = new JButton(ADD_CMD);
		addAttribButton.setActionCommand(ADD_CMD);
		addAttribButton.addActionListener(this);

		deleteButton = new JButton(DELETE_CMD);
		deleteButton.setActionCommand(DELETE_CMD);
		deleteButton.addActionListener(this);
 	 	
 		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
		buttonPanel.add(addAttribButton);
		buttonPanel.add(copyButton);
		buttonPanel.add(clearButton);
		buttonPanel.add(deleteButton);
		buttonPanel.add(saveButton);
		
		// now do the Grand layout of the whole panel
		this.setLayout( new BoxLayout(this, BoxLayout.PAGE_AXIS));
		typeNameInputPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
		this.add(typeNameInputPanel);
		this.add(Box.createRigidArea(new Dimension(0, 10)));
		allAttribScrollPane.setAlignmentX(JComponent.LEFT_ALIGNMENT);
		this.add(allAttribScrollPane);
		this.add(Box.createVerticalGlue());
		buttonPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
		this.add(buttonPanel);
		Color bc = this.getBackground();
		this.setBorder(BorderFactory.createMatteBorder(10,10,10,10, bc));
	}
	
	/**
	 * Given an input of a model, get all the data and display it in the panel
	 * @param model
	 */
	public void setModel( GameModel in ) {
		model = (UnitProfileModel)in;
		handleClearCmd();
		if ( model != null ) {
			deleteButton.setEnabled(true);
			copyButton.setEnabled(true);

			// an existing unit has just been set in here, need to populate the UI with what's in the model
			String name = model.getName();
			unTextField.setText(name);
			ArrayList allAttrib = model.getAttributes();
			for ( int i=0; i < allAttrib.size(); i++ ){
				UnitProfileAttribute attrib = (UnitProfileAttribute)allAttrib.get(i);
				AttributePanel attribP = handleAddAttributeCmd();	// add a new panel
				String attribName = attrib.getName();
				attribP.setName(attribName);
				
				// for ea attribP, setup the button to panel lookup relationship
				JButton deleteB = attribP.getDeleteB();
				attribPanelHash.put(deleteB, attribP);
				
				// depends on the class of the attrib, call different handler to set the value
				Object rawObj = attrib.getVal();
				attribP.setTypeObject(rawObj);
			}
		} else {
			// if model is null that indicates this is a editor node
			deleteButton.setEnabled(false);
			copyButton.setEnabled(false);
		}
		return;
	}
	
	/**
	 * Given 
	 * @return
	 */
	public UnitProfileModel getModel(){
		return( model );
	}
	
	/**
	 * Depending on which button is clicked, do the right thing.
	 */	
	public void actionPerformed(ActionEvent e) {
		String command = e.getActionCommand();
    
		if (CLEAR_CMD.equals(command)) {
			handleClearCmd();
			
		} else if (SAVE_CMD.equals(command)) {
			handleSaveCmd();
			
		} else if (COPY_CMD.equals(command)) {
			handleCopyCmd();
			
		} else if (ADD_CMD.equals(command)) {
			handleAddAttributeCmd();
		} else if (DELETE_CMD.equals(command)) {
			
			handleDeleteCmd();
		}
	}
	
	protected void handleClearCmd() {
		// delete all the attributes from the UI
		// 1) loop through all the attribPanel from the attributes ArrayList,
		// 2) for ea item, remove from the container, and from the ArrayList  
		// 3) also clear the entire attribPanelHash
		//
		unTextField.setText(null);
		int size = attributes.size();
		for (int i=0; i < size; i++) {
			AttributePanel attribPanel = (AttributePanel)attributes.get(i);
			allAttribPanel.remove(attribPanel);
		}
		attributes.clear();
		attribPanelHash.clear();
		
		// DOES NOT WORK model=null;
		this.validate();
	}

	/**
	 * If there already is a model, then we'll just write over what's in it.
	 * If there isn't currently a model, then we'll create a new one.
	 *
	 */
	protected void handleSaveCmd(){
		boolean isNew = false;
		if ( model == null ) {
			// the isMainEditor field is set to true because we want to set Model when selected
			model = new UnitProfileModel(null, GameModel.UNIT_PROFILE_EDITOR, false, this);
			isNew = true;
		} else {
			// remove all the old attributes 
			model.removeAllAttributes();
		}
		loadModelFromUI( attributes, model );
		
		DefaultMutableTreeNode tn = null;
		if ( isNew ) {
			tn = gameTreeEditor.nodeCreated(model);
			handleClearCmd();

		} else {
			// reload in case the name have changed
			tn = gameTreeEditor.getCurrentNode();
			gameTreeEditor.reloadTreeModel();
			//gameTreeEditor.nodeChanged();
		}
		
		// the notification is always done wether a brand new node or not, since 
		// new attributes might have been added.
		// tn = gameTreeEditor.getCurrentNode();
		gameTreeEditor.newProfileAdded(tn);

		// taken out for the profile with heirchy bug
		//model = null;
		return;
	}
	
	/**
	 * Take what's in the UI and move it into the model
	 *
	 */
	private void loadModelFromUI(ArrayList uiList, UnitProfileModel localMod) {
		String name = unTextField.getText();
		if (name == null) {
			name = DEFAULT_NAME;
		}
		localMod.setName(name);
		for ( int i=0; i < uiList.size(); i++ ){
			UnitProfileAttribute attribModel = new UnitProfileAttribute();
			AttributePanel attribP = (AttributePanel)uiList.get(i);
			String attribName = attribP.getName();
			attribModel.setName(attribName);
			Object attribObj = attribP.getTypeObject();
			attribModel.setVal(attribObj);
			localMod.addAttribute(attribModel);
		}
		return;		
	}
	/**
	 * Assume that if we get here, this has to be a leaf node
	 * 1. clear the UI, 
	 * 2. then call gameTreeEditor to delete this node
	 */
	protected void handleDeleteCmd() {
		handleClearCmd();
		DefaultMutableTreeNode tn = null;
		tn = gameTreeEditor.getCurrentNode();
		gameTreeEditor.profileDeleted(tn);
		gameTreeEditor.deleteCurrentNode();	
	}
	
	protected void handleCopyCmd(){
		boolean isNew = true;
		
		UnitProfileModel copyModel = new UnitProfileModel(null, GameModel.UNIT_PROFILE_EDITOR, false, this);
		loadModelFromUI( attributes, copyModel );
		String name = unTextField.getText();
		if (name != null) {
			// add "copy" to the end of the name
			name=name + "_copy";
		}
		copyModel.setName(name);		
		DefaultMutableTreeNode tn = gameTreeEditor.siblingNodeCreated(copyModel);
		gameTreeEditor.newProfileAdded(tn);
		return;
	}
	
	protected AttributePanel handleAddAttributeCmd() {
		// Create a new Attribute panel, and then add it to the allAttribPanel
		AttributePanel attribPanel = new AttributePanel(this);
		allAttribPanel.add(attribPanel);
		attributes.add(attribPanel);
		this.validate();
		return (attribPanel);
	}
	
	private final static String treeNodeString ="Unit Type Template".intern();
	public String toString() {
		return (treeNodeString);
	}
	
	/**
	 * Need to get ALL the childred (should be flat) userObject into a Object[]
	 * and then return it back to the caller
	 * 
	 * @return object[] array of userObject of the type GameTreeNode that's to be returned
	 */
	public Object[] getProfileList() {
		int childCount = editorTreeNode.getChildCount();
		Object[] res = new Object[childCount];
		for ( int i=0; i < childCount; i++ ) {
			res[i]=editorTreeNode.getChildAt(i);
		}
		return (res);
	}

	
	public class AttributePanel extends JPanel {
		
		protected JTextField anTextField;	// hold the name of the attribute
		protected int type;		// this is filled in when ever the user clicks on the check box
		
		public final String TYPE_NUMBER="num".intern();
		public final String TYPE_STRING="str".intern();
		public final String TYPE_RANGE="range".intern();
		
		JRadioButton numberB, stringB, rangeB;
		JButton deleteB;
		
		NumAttribPanel numPanel;
		StrAttribPanel strPanel;
		RangeAttribPanel rangePanel;
		JPanel attribValPanel; 
		ButtonGroup group;
		CardLayout cl;
		
		UnitProfileEditor editor;
		
		public AttributePanel(UnitProfileEditor editorIn){
			editor = editorIn;
			JLabel anLabel = new JLabel("Attrib Name: ");
			anTextField = new JTextField();
		
			// First setup the Attrib name Layout on top left
			JPanel attribNameInputPanel = new JPanel();
			attribNameInputPanel.setLayout(new BoxLayout(attribNameInputPanel, BoxLayout.LINE_AXIS));
			attribNameInputPanel.add(anLabel);
			attribNameInputPanel.add(anTextField);
		
			// now need to set some size limitations to the components
			Dimension d = anTextField.getPreferredSize();
			d.width = 50;
			anTextField.setMinimumSize(d);
			anTextField.setPreferredSize(d);
			anTextField.setMaximumSize(d);
			
			// now setup the Left component which has the delete button on the bottom of the attribNameInputPanel
			JPanel leftPanel = new JPanel();

			leftPanel.setLayout( new BoxLayout(leftPanel, BoxLayout.PAGE_AXIS ));
			deleteB = new JButton( "Delete");
			leftPanel.add(attribNameInputPanel);
			leftPanel.add(Box.createRigidArea(new Dimension(0,5)));
			leftPanel.add(deleteB); 
			
			// Now setup the Radio Buttons
			numberB = new JRadioButton("Number");
			numberB.setActionCommand(TYPE_NUMBER);
			numberB.setSelected(true);
			stringB = new JRadioButton("String");
			stringB.setActionCommand(TYPE_STRING);
			rangeB = new JRadioButton("Range");
			rangeB.setActionCommand(TYPE_RANGE);
			group = new ButtonGroup();
			group.add(numberB);
			group.add(stringB);
			group.add(rangeB);
			
			// Layout the radio buttons 
			JPanel radioPanel = new JPanel();
//			// DEBUG
//			radioPanel.setBorder(BorderFactory.createLineBorder(Color.YELLOW));

			radioPanel.setLayout( new BoxLayout(radioPanel, BoxLayout.LINE_AXIS ));
			radioPanel.add(numberB);
			radioPanel.add(Box.createRigidArea(new Dimension(5,0)));
			radioPanel.add(stringB);
			radioPanel.add(Box.createRigidArea(new Dimension(5,0)));
			radioPanel.add(rangeB);
			
			// layout a card layout right below the radio buttons
			attribValPanel = new JPanel();
//			// DEBUG
//			attribValPanel.setBorder(BorderFactory.createLineBorder(Color.RED));
			attribValPanel.setLayout(new CardLayout());
			numPanel = new NumAttribPanel();
			strPanel = new StrAttribPanel();
			rangePanel = new RangeAttribPanel();
			attribValPanel.add(numPanel, TYPE_NUMBER);
			attribValPanel.add(strPanel, TYPE_STRING);
			attribValPanel.add(rangePanel, TYPE_RANGE);
			attribValPanel.validate();
			
			// always show number type by default
			cl = (CardLayout)(attribValPanel.getLayout());
			cl.show(attribValPanel, TYPE_NUMBER);

			// now layout the radio and the attribVal
			JPanel rightPanel = new JPanel();
//			// DEBUG
//			rightPanel.setBorder(BorderFactory.createLineBorder(Color.GREEN));
			rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.PAGE_AXIS));
			radioPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
			rightPanel.add(radioPanel);
			radioPanel.add(Box.createRigidArea(new Dimension(0,5)));
			attribValPanel.setAlignmentX(JComponent.LEFT_ALIGNMENT);
			rightPanel.add(attribValPanel);
			
			this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
			leftPanel.setAlignmentY(JComponent.TOP_ALIGNMENT);
			this.add( leftPanel );
			this.add(Box.createRigidArea(new Dimension(5,0)));
			rightPanel.setAlignmentY(JComponent.TOP_ALIGNMENT);
			this.add( rightPanel );
			this.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
			
			// hook up radio button action listener
			numberB.addActionListener(new ActionListener()  {
				public void actionPerformed(ActionEvent e) {	
					CardLayout cl = (CardLayout)(attribValPanel.getLayout());
					cl.show(attribValPanel, TYPE_NUMBER);							  
			}});
			stringB.addActionListener(new ActionListener()  {
				public void actionPerformed(ActionEvent e) {	
					CardLayout cl = (CardLayout)(attribValPanel.getLayout());
					cl.show(attribValPanel, TYPE_STRING);							  
			}});			
			rangeB.addActionListener(new ActionListener()  {
				public void actionPerformed(ActionEvent e) {	
					CardLayout cl = (CardLayout)(attribValPanel.getLayout());
					cl.show(attribValPanel, TYPE_RANGE);							  
			}});
			
			// delete panel handling
			deleteB.addActionListener(new ActionListener()  {
				public void actionPerformed(ActionEvent e) {
					Object src = e.getSource();
					AttributePanel ap = (AttributePanel)attribPanelHash.get(src);
					if ( ap != null ){
						attribPanelHash.remove(src);
						allAttribPanel.remove(ap);
						attributes.remove(ap);
						editor.validate();
					}
			}});			
		}
		/**
		 * FInd out the name of this attribute
		 * @return String name
		 */
		public String getName() {
			String name = anTextField.getText();
			if ( name == null ) {
				name = "attribute name";
			}
			return ( name );
		}
		
		public void setName(String in){
			anTextField.setText( in );
		}
		
		/**
		 * Find out which button is pushed
		 * @return TYPE_NUMBER, TYPE_STRING, or TYPE_RANGE
		 */
		public Object getTypeObject() {
			ButtonModel bm = group.getSelection();
			String selected= bm.getActionCommand();
			Object val = null;
			if ( selected.equals(TYPE_NUMBER)) {
				val = numPanel.getObject();
			} else if ( selected.equals(TYPE_STRING)) {
				val = strPanel.getObject();
			} else if ( selected.equals(TYPE_RANGE)) {
				val = rangePanel.getObject();
			}
			return (val);
		}
		
		public void setTypeObject( Object rawObj ) {
			if ( rawObj instanceof Integer ) {
				numPanel.setObject((Integer)rawObj);
				numberB.setSelected(true);
				cl.show(attribValPanel, TYPE_NUMBER);
				
			} else if ( rawObj instanceof String ) {
				strPanel.setObject( (String)rawObj );
				stringB.setSelected(true);
				cl.show(attribValPanel, TYPE_STRING);
				
			} else if ( rawObj instanceof RangeUnitProfileAttribute ) {
				rangePanel.setObject((RangeUnitProfileAttribute)rawObj );
				rangeB.setSelected(true);
				cl.show(attribValPanel, TYPE_RANGE);				
			}				
		}
		/**
		 * @return Returns the deleteB.
		 */
		public JButton getDeleteB() {
			return deleteB;
		}
	}
	
	public class AttribValuePanel extends JPanel {
		public AttribValuePanel() {
			Dimension d = new Dimension(300, 40);
			this.setMinimumSize(d);
			this.setPreferredSize(d);
			this.setMaximumSize(d);
		}	
	}
	
	// this one will contain a JSpinner that contains a number from 0 to 999
	public class NumAttribPanel extends AttribValuePanel {
		SpinnerNumberModel  model;
		
		public NumAttribPanel() {
			super();
			model = new SpinnerNumberModel(0, -999, 999, 1);
			JSpinner spinner = new JSpinner(model);
			this.setLayout(new FlowLayout(FlowLayout.LEADING));
			this.add( spinner );
		}
		
		public Object getObject() {
			return ( model.getNumber());					
		}
		
		public void setObject( Integer in ) {
			model.setValue(in);
		}

	}
	
	public class StrAttribPanel  extends AttribValuePanel {
		JTextField textField;
		
		public StrAttribPanel() {
			super();
			textField = new JTextField();
			Dimension d = textField.getPreferredSize();
			d.width = 50;
			textField.setMinimumSize(d);
			textField.setPreferredSize(d);
			textField.setMaximumSize(d);
			this.setLayout(new FlowLayout(FlowLayout.LEADING));
			this.add(textField);
		}
		
		public Object getObject() {
			String val = textField.getText();
			if ( val.equals(null)) {
				val = "default";
			}
			return ( val );					
		}
		
		public void setObject( String in ) {
			textField.setText( in );
		}

	}
	
	public class RangeAttribPanel  extends AttribValuePanel {
		SpinnerNumberModel loModel, hiModel, incrModel;
		
		public  RangeAttribPanel() {
			super();
			loModel = new SpinnerNumberModel(0, -999, 999, 1);
			hiModel = new SpinnerNumberModel(0, -999, 999, 1);
			incrModel = new SpinnerNumberModel(1, -999, 999, 1);
			JSpinner loSpinner = new JSpinner(loModel);
			JSpinner hiSpinner = new JSpinner(hiModel);
			JSpinner incrSpinner = new JSpinner(incrModel);
			JLabel loLabel = new JLabel("Lo: ");			
			JLabel hiLabel = new JLabel("Hi: ");
			JLabel incrLabel = new JLabel("Step: ");
			this.setLayout(new FlowLayout(FlowLayout.LEADING));
			this.add(loLabel);
			this.add(loSpinner);
			this.add(Box.createRigidArea(new Dimension(3,0)));
			this.add(hiLabel);
			this.add(hiSpinner);
			this.add(Box.createRigidArea(new Dimension(3,0)));
			this.add(incrLabel);
			this.add(incrSpinner);
		}
		
		public Object getObject() {
			RangeUnitProfileAttribute val = new RangeUnitProfileAttribute();
			val.setMinVal((Integer)loModel.getNumber());
			val.setMaxVal((Integer)hiModel.getNumber());
			val.setStepVal((Integer)incrModel.getNumber()); 
			val.setInitVal((Integer)hiModel.getNumber());
			return ( val );
		}
		
		public void setObject( RangeUnitProfileAttribute in ) {
			loModel.setValue(in.getMinVal());
			hiModel.setValue(in.getMaxVal());
			incrModel.setValue(in.getStepVal());
			return;
		}
	}
	/**
	 * @param node
	 */
	public void setEditorNode(DefaultMutableTreeNode node) {
		editorTreeNode = node;
	}

}
